#include "stdafx.h"

#include "parameters_model.h"
#include "parameter_editor.h"
#include "units.h"

QModelIndex getIndex(const QModelIndex &old_index, typeCol_t type){
	QModelIndex new_index(old_index);
	new_index.c = type;
	return new_index;
}

ParameterItem::ParameterItem(const QVector<QVariant> &data, ParameterItem *parent){
	parentItem = parent;
	itemData = data;
}

ParameterItem::~ParameterItem(){
	qDeleteAll(childItems);
}

ParameterItem *ParameterItem::child(int number){
	return childItems.value(number);
}

int ParameterItem::childCount() const{
	return childItems.count();
}

int ParameterItem::childNumber() const{
	if (parentItem)
		return parentItem->childItems.indexOf(const_cast<ParameterItem*>(this));
	return 0;
}

int ParameterItem::columnCount() const{
	return itemData.count();
}

QVariant ParameterItem::data(int column) const{
	return itemData.value(column);
}

bool ParameterItem::insertChildren(int position, int count, int columns){
	if (position < 0 || position > childItems.size())
		return false;
	for (int row = 0; row < count; ++row) {
		QVector<QVariant> data(columns);
		ParameterItem *item = new ParameterItem(data, this);
		childItems.insert(position, item);
	}

	return true;
}

bool ParameterItem::removeChildren(int position){
	if (position < 0 || position > childItems.size())
		return false;
	childItems.removeAt(position);
	return true;
}

ParameterItem *ParameterItem::parent(){
	return parentItem;
}

bool ParameterItem::setData(int column, const QVariant &value){
	if (column < 0 || column >= itemData.size())
		return false;
	itemData[column] = value;
	return true;
}

ParametersModel::ParametersModel(QObject *parent)
	: QAbstractItemModel(parent)
{
	QStringList headers;
	headers << "Name" << "Value"<< "Unit"<< "Type" << "State" << "Description" << "Items" << "Script";
	QVector<QVariant> rootData;
	foreach (QString header, headers)
		rootData << header;

	rootItem = new ParameterItem(rootData);
}

ParametersModel::~ParametersModel(){
	delete rootItem;
}

void ParametersModel::addItem(const QString &script, const QString &name, const QVariant &value, const QString &units, const int &type,
	const int &state, const QString &description, const bool &child, const bool &expanded) const
{
	ParameterItem *parent;
	QString parameter_name;
	if (name.split("/").size() > 1){
		QString parent_name = name.split("/")[0];
		parameter_name = name.split("/")[1];
		QModelIndexList list = this->match(this->index(0, 0), Qt::DisplayRole, QVariant(parent_name), 1,
			Qt::MatchFlags(Qt::MatchRecursive | Qt::MatchFixedString | Qt::MatchStartsWith));
		if (list.size() == 0){
			addItem(script, parent_name, "", "", 3, 0);
			list = this->match(this->index(0, 0), Qt::DisplayRole, QVariant(parent_name), 1,
				Qt::MatchFlags(Qt::MatchRecursive | Qt::MatchFixedString | Qt::MatchStartsWith));
		}
		parent = rootItem->child(list[0].row());
	}else {
		parent = rootItem;
		parameter_name = name;
	}
	parent->insertChildren(parent->childCount(), 1, rootItem->columnCount());
	parent->child(parent->childCount() - 1)->setData(0, parameter_name);
	if (type == 0){
		if(value == true) parent->child(parent->childCount() - 1)->setData(1, "Yes");
		else parent->child(parent->childCount() - 1)->setData(1, "No");
	}else if (type == 4){
		QStringList list = value.toString().split(";");
		parent->child(parent->childCount() - 1)->setData(1, list[list[0].toInt() + 1]);
		parent->child(parent->childCount() - 1)->setData(6, value);
	}else if (type == 5){
		QString row_count;
		if (value.toString().split(" : ").front() != "func"){
			QStringList list = value.toString().split("/").back().split(";");
			row_count = QString::number(list.count());
			if (list.count() == 1 && list[0] == "") row_count = "0";
			parent->child(parent->childCount() - 1)->setData(1, "count: " + row_count);
		}else{
			row_count = value.toString().split(" : ").back();
		}
		parent->child(parent->childCount() - 1)->setData(1, "count: " + row_count);
		parent->child(parent->childCount() - 1)->setData(6, value);
	}else parent->child(parent->childCount() - 1)->setData(1, value);

	parent->child(parent->childCount() - 1)->setData(2, units);
	parent->child(parent->childCount() - 1)->setData(3, type);
	parent->child(parent->childCount() - 1)->setData(4, state);
	parent->child(parent->childCount() - 1)->setData(5, description);
	parent->child(parent->childCount() - 1)->setData(7, script);
	
}

void ParametersModel::removeItems(const QString &script){
	int i = rootItem->childCount() - 1;
	while (this->index(i, 0).isValid()){
		if (getIndex(this->index(i, 0), SCRIPT).data().toString() == script)
			rootItem->removeChildren(i);
		--i;
	}
}

int ParametersModel::columnCount(const QModelIndex & /* parent */) const{
	return rootItem->columnCount();
}

QVariant ParametersModel::data(const QModelIndex &index, int role) const{
	if (!index.isValid())
		return QVariant();
	if (role != Qt::DisplayRole && role != Qt::EditRole)
		return QVariant();
	if (role == Qt::FontRole){
		QFont boldFont;
		boldFont.setBold(true);
		return boldFont;
	}
	ParameterItem *item = getItem(index);
	return item->data(index.column());
}

Qt::ItemFlags ParametersModel::flags(const QModelIndex &index) const{
	if (!index.isValid())
		return 0;
	return Qt::ItemIsEditable | Qt::ItemIsEnabled | Qt::ItemIsSelectable;
}

ParameterItem *ParametersModel::getItem(const QModelIndex &index) const{
	if (index.isValid()) {
		ParameterItem *item = static_cast<ParameterItem*>(index.internalPointer());
		if (item) return item;
	}
	return rootItem;
}

QVariant ParametersModel::headerData(int section, Qt::Orientation orientation,
	int role) const{
		if (orientation == Qt::Horizontal && role == Qt::DisplayRole)
			return rootItem->data(section);

		return QVariant();
}

QModelIndex ParametersModel::index(int row, int column, const QModelIndex &parent) const{

	if (parent.isValid() && parent.column() != 0)
		return QModelIndex();

	ParameterItem *parentItem = getItem(parent);

	ParameterItem *childItem = parentItem->child(row);
	if (childItem)
		return createIndex(row, column, childItem);
	else
		return QModelIndex();
}

QModelIndex ParametersModel::parent(const QModelIndex &index) const{

	if (!index.isValid())
		return QModelIndex();

	ParameterItem *childItem = getItem(index);
	ParameterItem *parentItem = childItem->parent();

	if (parentItem == rootItem)
		return QModelIndex();
	return createIndex(parentItem->childNumber(), 0, parentItem);
}

int ParametersModel::rowCount(const QModelIndex &parent) const{
	ParameterItem *parentItem = getItem(parent);
	return parentItem->childCount();
}

bool ParametersModel::setData(const QModelIndex &index, const QVariant &value,
	int role)
{
	if (role != Qt::EditRole)
		return false;
	ParameterItem *item = getItem(index);
	bool result = item->setData(index.column(), value);
	if (result)
		emit dataChanged(index, index);
	return result;
}

int ParametersModel::dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight){
	if (topLeft.r == bottomRight.r && topLeft.c ==1 && bottomRight.c == 1){
		QVariant v;
		if (getIndex(topLeft, TYPE).data().toInt() == 0){
			if(topLeft.data() == "Yes") v = true; else v = false;
		}else if (getIndex(topLeft, TYPE).data().toInt() == 4){
			QString cur_item = topLeft.data(Qt::DisplayRole).toString();
			QStringList list = getIndex(topLeft, DATA).data(Qt::DisplayRole).toString().split(";");
			v = getComboboxDataString(cur_item, list);
		}else if (getIndex(topLeft, TYPE).data().toInt() == 5){
			v = getIndex(topLeft, DATA).data(Qt::DisplayRole).toString();
		}else v = topLeft.data();

		emit ItemChanged(getIndex(topLeft, NAME).data().toString(), v);
	}
	return 0;
}

void ParametersModel::changeData(const QString &name, const QVariant &value){
	QModelIndexList list = this->match(this->index(0, 0), Qt::DisplayRole, QVariant(name), 1,
		Qt::MatchFlags(Qt::MatchRecursive | Qt::MatchFixedString | Qt::MatchStartsWith));
	if (!list.size()) return;
	QModelIndex index = list[0];
	int type = getIndex(index, TYPE).data().toInt();
	QVariant v;
	if (type == 0){
		if(value == true) v = "Yes"; else v = "No";
	}else if (type == 4){
		QStringList list = value.toString().split(";");
		v = list[list[0].toInt() + 1];
	}else if (type == 5){
		v = "count: " + value.toString().split(" : ").back();
	}else v = value;
	
	setData(getIndex(index, VALUE), v);
	if (type == 4 || type == 5) setData(getIndex(index, DATA), value);

	emit update();
}

void ParametersModel::setDescription(QSharedPointer<QLabel> descr_label, QModelIndex sel_index){
	descr_label->setText("description:\n" + getIndex(sel_index, DESCR).data().toString());
}

QStringList ParametersModel::getAllParameters(){
	QStringList list;
	int i = 0;
	QString script, name, value, unit;
	while (index(i,0).isValid()){
		script = getIndex(index(i,0), SCRIPT).data().toString();
		name = getIndex(index(i,0), NAME).data().toString();
		if (getIndex(index(i, 0), TYPE).data().toInt() == 5)
			value = getIndex(index(i,0), DATA).data().toString();
		else
			value = getIndex(index(i,0), VALUE).data().toString();
		unit = getIndex(index(i,0), UNIT).data().toString();
		list.push_back(script + " ; " + name + " ; " + value + " ; " + unit);
		int j = 0;
		while (index(i,0).child(j, 0).isValid()){
			script = getIndex(index(i,0).child(j, 0), SCRIPT).data().toString();
			name = getIndex(index(i,0).child(j, 0), NAME).data().toString();
			if (getIndex(index(i,0).child(j, 0), TYPE).data().toInt() == 5)
				value = getIndex(index(i,0).child(j, 0), DATA).data().toString();
			else
				value = getIndex(index(i,0).child(j, 0), VALUE).data().toString();
			unit = getIndex(index(i,0).child(j, 0), UNIT).data().toString();
			list.push_back(script + " ; " + name + " ; " + value + " ; " + unit);
			j++;
		}
		i++;
	}
	return list;
}

QModelIndex ParametersModel::getParameter(const QString &name, const QString &script){
	QModelIndexList list = this->match(this->index(0, 0), Qt::DisplayRole, QVariant(name), 10e3,
		Qt::MatchFlags(Qt::MatchRecursive | Qt::MatchFixedString | Qt::MatchStartsWith));
	QString s ;
	foreach (QModelIndex i, list){
		s = getIndex(i, SCRIPT).data().toString();
		if (getIndex(i, SCRIPT).data().toString() == script)
			return i;
	}
	QMessageBox::critical(0, "Error", QString("Parameter %1 is not found!").arg(name),	QMessageBox::Ok);	
	return QModelIndex();
}

QString ParametersModel::getStringParameter(const QString &name, const QString &script){
	QModelIndex index = getParameter(name, script);
	if (!index.isValid()) return "";
	//QString data;
	if(getIndex(index, TYPE).data() == 5){
		return getIndex(index, DATA).data().toString();
	}else{
		return getIndex(index, VALUE).data().toString();
	}
	//return data;
}

double ParametersModel::getNumParameter(const QString &name, const QString &script){
	QModelIndex index = getParameter(name, script);
	if (!index.isValid()) return -1;
	if (getIndex(index, TYPE).data().toInt() == 5 ){
		QMessageBox::critical(0, "Error", QString("Parameter %1 is not constant!").arg(name),	QMessageBox::Ok);	
		return -1;
	}
	double value = getIndex(index, VALUE).data().toDouble();
	QString unit = getIndex(index, UNIT).data().toString();
	Units *unit_c = new Units;
	double coeff = unit_c->getCoeff(unit);
	return value * coeff;
}

double ParametersModel::getTimeVaryingParameter(const QString &name, const double &cur_time, const QString &script){
	QModelIndex index = getParameter(name, script);
	if (!index.isValid()) return -1;
	if (getIndex(index, TYPE).data().toInt() != 5 ){
		QMessageBox::critical(0, "Error", QString("Parameter %1 is constant!").arg(name),	QMessageBox::Ok);	
		return -1;
	}

	QVector<double> time, parameter_value;
	QString all_data = getIndex(index, DATA).data().toString();
	QStringList data = all_data.split("/").back().split(";");
	foreach (QString row, data){
		time.push_back(row.split(":").front().toDouble());
		parameter_value.push_back(row.split(":").back().toDouble());
	}
	time.push_back(time.back());
	parameter_value.push_back(parameter_value.back());
	double value = 0;
	if (time.size()){
		int first_index = 0;
		int last_index = time.size() - 1;
		while (last_index - first_index > 1){
			int middle_index = (last_index + first_index) / 2;
			if (cur_time > time[middle_index]){
				first_index = middle_index;
			}else{
				last_index = middle_index;
			}
		}
		QString type_of_depending = all_data.split("/")[0].split(":").back();
		
		double coeff = type_of_depending == "piecewise-constant" || time[last_index] ==  time[first_index] ? 0 : (cur_time - time[first_index]) / (time[last_index] - time[first_index]);
		value = parameter_value[first_index] + (parameter_value[last_index] - parameter_value[first_index]) * coeff;

		QString unit = all_data.split("/")[2].split(":").back();
		Units *unit_c = new Units;
		value *= unit_c->getCoeff(unit);
		if (cur_time > 6){
			int r = 0;
		}
	}
	
	return value;
}


void ParametersModel::setAllParameters(const QStringList &list){
	for (int i = 0; i < list.count(); i++){
		QStringList parameter_data = list[i].split(" ; ");
		QModelIndex index = getParameter(parameter_data[1], parameter_data[0]);
		if (getIndex(index, TYPE).data().toInt() == 5){
			QStringList list = parameter_data[2].split("/").back().split(";");
			QString row_count = QString::number(list.count());
			if (list.count() == 1 && list[0] == "") row_count = "0";
			this->setData(getIndex(index, VALUE), "count: " + row_count);
			this->setData(getIndex(index, DATA), parameter_data[2]);
		}else
			this->setData(getIndex(index, VALUE), parameter_data[2]);
		this->setData(getIndex(index, UNIT), parameter_data[3]);
		emit ItemChanged(parameter_data[1], parameter_data[2]);
	}

}

QWidget *Delegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const{
	if (index.column() == 0) {
		QLabel *label = new QLabel(parent);
		return label;
	}else if (index.column() == 1) {
		int type = getIndex(index, TYPE).data(Qt::DisplayRole).toInt();
		QVariant val = index.model()->data(index, Qt::DisplayRole);
		if (type == 0){
			QComboBox *editor = new QComboBox(parent);
			editor->addItem("Yes");
			editor->addItem("No");
			return editor;
		} else if (type == 1){
			QDoubleSpinBox *spinbox = new QDoubleSpinBox(parent);
			spinbox->setMaximum(1e6);
			spinbox->setMinimum(-1e6);
			spinbox->setValue(val.toDouble());
			return spinbox;
		} else if (type == 2){
			QLineEdit *editor = new QLineEdit(parent);
			editor->setText(index.model()->data(index, Qt::DisplayRole).toString());
			return editor;
		} else if (type == 4){
			QStringList list = getIndex(index, DATA).data(Qt::DisplayRole).toString().split(";");
			QComboBox *editor = new QComboBox(parent);
			for(int i = 1; i < list.count(); i++)
				editor->addItem(list[i]);
			editor->setCurrentIndex(list[0].toInt());
			return editor;
		}
	}else{	
		if(getIndex(index, TYPE).data().toInt() != 5){
			QComboBox *editor = new QComboBox(parent);
			QString currentText = index.model()->data(index, Qt::DisplayRole).toString();
			if (currentText != ""){
				Units *unitlist = new Units();
				QStringList list = unitlist->getUnitList(currentText);
				editor->clear();
				foreach(QString unit, list)
					editor->addItem(unit);
				for(int i = 0; i < editor->count(); i++)
					if (currentText == editor->itemText(i)){
						editor->setCurrentIndex(i);
						break;
					}
			}else editor->setEnabled(false);
			return editor;
		}else{
			QPushButton *button = new QPushButton(parent);
			button->setText(index.model()->data(index, Qt::DisplayRole).toString());
			//QString a = getIndex(index, DATA).data().toString();
			//string_data = a;
			//emit data("D");
			connect(button, SIGNAL(clicked()), this, SLOT(paremeterEditorShow()));
			return button;
		}
	}
	return NULL;
}

void Delegate::setEditorData(QWidget *editor, const QModelIndex &index) const{
	if (index.column() == 1) {
		QModelIndex indexLC(index);
		indexLC.c = 3;
		int type = indexLC.model()->data(indexLC, Qt::DisplayRole).toInt();
		QVariant val = index.model()->data(index, Qt::DisplayRole);
		if (type == 0 || type == 4){
			QComboBox *comboEditor = qobject_cast<QComboBox *>(editor);
		} else if (type == 1){
			QDoubleSpinBox *valueEditor = qobject_cast<QDoubleSpinBox *>(editor);
			valueEditor->setValue(index.model()->data(index, Qt::EditRole).toDouble());
		}else if (type == 2){
			QLineEdit *textEditor = qobject_cast<QLineEdit *>(editor);
			textEditor->setText(index.model()->data(index, Qt::EditRole).toString());
		}
	}else{
		QModelIndex indexUnit(index);
		indexUnit.c = 2;
		if(indexUnit.data().toInt() != 5){
			QComboBox *comboEditor = qobject_cast<QComboBox *>(editor);
			if (comboEditor) {
				QString currentText = comboEditor->currentText();
				Units *unitlist = new Units();
				QStringList list = unitlist->getUnitList(currentText);
				comboEditor->clear();
				foreach(QString unit, list)
					comboEditor->addItem(unit);

				for(int i = 0; i < comboEditor->count(); i++)
					if (currentText == comboEditor->itemText(i)){
						comboEditor->setCurrentIndex(i);
						break;
					}
					comboEditor->setEnabled(comboEditor->count() > 1);
			}
		}else{
			QPushButton *button = new QPushButton(editor);
		}
	}
}

void Delegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const{
	if (index.column() == 1) {
		int type = getIndex(index, TYPE).data(Qt::DisplayRole).toInt();
		QVariant val = index.data(Qt::DisplayRole);
		if (type == 0 || type == 4){
			QComboBox *comboEditor = qobject_cast<QComboBox *>(editor);
			model->setData(index, comboEditor->currentText());
			if (type == 4){

				QString cur_item = index.data(Qt::DisplayRole).toString();
				QStringList list = getIndex(index, DATA).data(Qt::DisplayRole).toString().split(";");
				model->setData(getIndex(index, DATA), getComboboxDataString(cur_item, list));
			}
		} else if (type == 1){
			QDoubleSpinBox *valueEditor = qobject_cast<QDoubleSpinBox *>(editor);
			model->setData(index, valueEditor->value());
		}else if (type == 2){
			QLineEdit *textEditor = qobject_cast<QLineEdit *>(editor);
			model->setData(index, textEditor->text());
		}
	}else{
		QComboBox *comboEditor = qobject_cast<QComboBox *>(editor);
		if (comboEditor) {
			model->setData(index, comboEditor->currentText());
		}
	}
}

void Delegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const{
	painter->save();
	QPen pen(Qt::lightGray);
	pen.setWidth(1);
	painter->setPen(pen);
	QRect rc(option.rect);
	painter->drawLine(QLine(rc.right(), rc.top(), rc.right(), rc.bottom()));
	painter->drawLine(QLine(0, rc.bottom(), rc.right(), rc.bottom()));
	painter->restore();
	QItemDelegate::paint(painter, option, index);
}


QString getComboboxDataString(QString cur_item, QStringList list){
	QString str;
	for (int i = 1; i < list.count(); i++)
		if (cur_item == list[i]){
			str = QString::number(i - 1);
			break;
		}
		if (str == "") str = "0";
		for (int i = 1; i < list.count(); i++)
			str += ";" + list[i];
		return str;
}

void Delegate::paremeterEditorShow(){
/*
	Editor editor(new QWidget());
	QString data = getIndex(common.data()->getParametersWidget().data()->currentIndex(), DATA).data().toString();
	if (data.split(" : ").front() != "func"){
		editor.setData(getIndex(common.data()->getParametersWidget().data()->currentIndex(), NAME).data().toString(),
			getIndex(common.data()->getParametersWidget().data()->currentIndex(), DATA).data().toString());
		if (editor.exec()){
			QString data = editor.getData();
			QString count = QString::number(data.split("/").back().split(";").count());
			if (data.split("/").back() == "") count = "0";
			common.data()->getParametersWidget().data()->model()->setData(getIndex(common.data()->getParametersWidget().data()->currentIndex(), DATA), data);
			common.data()->getParametersWidget().data()->model()->setData(getIndex(common.data()->getParametersWidget().data()->currentIndex(), VALUE), "count: " + count);
		}
	}else{
		QString parameter_name = getIndex(common.data()->getParametersWidget().data()->currentIndex(), NAME).data().toString();
		emit parent_func(parameter_name);
	}
	*/
	Editor editor(new QWidget());
	QString data = getIndex(tree_widget.data()->currentIndex(), DATA).data().toString();
	QString name = getIndex(tree_widget.data()->currentIndex(), NAME).data().toString();
	if (data.split(" : ").front() != "func"){
		editor.setData(name, data);
		if (editor.exec()){
			QString data = editor.getData();
			QString count = QString::number(data.split("/").back().split(";").count());
			if (data.split("/").back() == "") count = "0";
			tree_widget.data()->model()->setData(getIndex(tree_widget.data()->currentIndex(), DATA), data);
			tree_widget.data()->model()->setData(getIndex(tree_widget.data()->currentIndex(), VALUE), "count: " + count);
		}
	}else{
		emit parent_func(name);
	}
}

